package com.zishuimuyu.job.upload;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.watch.SimpleWatcher;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.Watcher;
import cn.hutool.core.io.watch.watchers.DelayWatcher;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.ftp.Ftp;
import cn.hutool.extra.ftp.FtpConfig;
import cn.hutool.extra.ftp.FtpMode;
import cn.hutool.extra.ssh.Sftp;
import cn.hutool.setting.Setting;
import cn.hutool.setting.SettingUtil;
import com.zishuimuyu.utills.SaveRecordsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 文件监听-本任务只用来上传文件
 *
 * @author：GJH
 * @date 2021年10月15日
 * @company：北京木联能软件股份有限公司
 */
@Slf4j
@Component
public class UploadFileJobByWatch {
    // private static Props uploadFileProperties = new Props("uploadFile.properties");
    private static Setting uploadFileSetting = SettingUtil.getFirstFound("config/uploadFile.setting");
    // private static List<String> types = new ArrayList<>();
    private static Map<String, String> fileTypes = uploadFileSetting.getMap("fileTypes");
    private static String localPath = "";
    private static String remotePath = "";
    private static String countFilePath = "";
    private static String filePrefix = "";
    private static String fileExtension = "";
    private static String delete = "";

    //静态代码块会在依赖注入后自动执行,并优先执行 下载
    static {
        localPath = uploadFileSetting.getStr("localPath");
        remotePath = uploadFileSetting.getStr("remotePath");
        countFilePath = uploadFileSetting.getStr("countFilePath");
        filePrefix = uploadFileSetting.getStr("filePrefix");
        fileExtension = uploadFileSetting.getStr("fileExtension");
        delete = uploadFileSetting.getStr("delete");
    }

    /**
     * 将要执行的方法所在的类交个spring容器扫描(@Component),并且在要执行的方法上添加@PostConstruct注解或者静态代码块执行**
     *
     * @Postcontruct’在依赖注入完成后自动调用
     */
    @PostConstruct
    public static void upload() {
        // log.info("@Postcontruct’在依赖注入完成后自动调用");
        if ("false".equals(uploadFileSetting.get("watcherUploadJobEnable"))) {
            //未启用上传任务,返回
            log.info("监听上传任务未启用：{}-> {}");
            return;
        }
        log.info("监听上传任务启用：{}-> {}", localPath, remotePath);
        String protocol = uploadFileSetting.getStr("protocol");
        String host = uploadFileSetting.getStr("host");
        String charset = uploadFileSetting.getStr("Charset");
        int defaultPort = "sftp".equalsIgnoreCase(protocol) ? 22 : 21;
        Integer port = uploadFileSetting.getInt("port", defaultPort);
        String userName = uploadFileSetting.getStr("userName");
        String passWord = uploadFileSetting.getStr("passWord");
        Long sessionConnectTimeout = uploadFileSetting.getLong("sessionConnectTimeout",2000L);
        Long channelConnectedTimeout = uploadFileSetting.getLong("channelConnectedTimeout",2000L);
        FtpConfig ftpConfig = new FtpConfig(host, port, userName, passWord, CharsetUtil.charset(charset));
        ftpConfig.setConnectionTimeout(sessionConnectTimeout);
        ftpConfig.setSoTimeout(channelConnectedTimeout);


        //启动监听事件,监听本地文件文件夹
        Watcher watcher = new SimpleWatcher() {
            //监听文件创建事件
            @Override
            public void onCreate(WatchEvent<?> event, Path currentPath) {
                Object obj = event.context();
                String fileName = Convert.toStr(obj);
                //等待5秒(等待文件完全生成)
                ThreadUtil.safeSleep(5000);

                List<File> files = FileUtil.loopFiles(localPath);

                if (!"*".equals(filePrefix)) {
                    //根据filePrefix 前缀过滤
                    files = files.stream().filter(file -> file.getName().startsWith(filePrefix)).collect(Collectors.toList());
                }
                if (!"*".equals(fileExtension)) {
                    //根据fileExtension 后缀过滤
                    files = files.stream().filter(file -> file.getName().endsWith(fileExtension)).collect(Collectors.toList());
                }
                if (CollUtil.isEmpty(files)) {
                    return;
                }
                boolean isDelete = StrUtil.equalsIgnoreCase("true", delete);
                List<String> fileNameList = new ArrayList<>();
                if ("sftp".equalsIgnoreCase(protocol)) {
                    fileNameList = sftpUpload(ftpConfig, remotePath, files, isDelete);
                }
                if ("ftp".equalsIgnoreCase(protocol)) {
                    fileNameList = ftpUpload(ftpConfig, remotePath, files, isDelete);
                }
                File iniFile = FileUtil.file(countFilePath, "uploadedFileNames.ini");
                SaveRecordsUtil.saveUploadLog(iniFile, fileNameList, fileTypes);

            }
        };
        //延迟处理监听事件 在监听目录或文件时，如果这个文件有修改操作，JDK会多次触发modify方法，为了解决这个问题，我们定义了DelayWatcher，此类通过维护一个Set将短时间内相同文件多次modify的事件合并处理触发，从而避免以上问题
        WatchMonitor monitor = WatchMonitor.createAll(localPath, new DelayWatcher(watcher, 10000));
        //之间听本级目录,不涉及下级目录
        monitor.setMaxDepth(0);
        monitor.start();
    }

    /**
     * sftp上传
     *
     * @param config
     * @param files
     * @return java.util.List<java.lang.String>
     */
    public static List<String> sftpUpload(FtpConfig config,String remotePath, List<File> files, boolean isDelete) {
        boolean uploadResult = false;
        List<String> fileNameList = new ArrayList<>();
        Sftp sftp = new Sftp(config);
        for (File file : files) {
            uploadResult = sftp.upload(remotePath, file);
            if (uploadResult) {
                log.info("SFTP监听上传成功：{}-> {}", file.getAbsolutePath(), remotePath + "/" + file.getName());
                fileNameList.add(file.getName());
                if (isDelete) {
                    FileUtil.del(file);
                }
            }
        }
        IoUtil.close(sftp);
        return fileNameList;
    }

    /**
     * ftp上传
     *
     * @param config
     * @param files
     * @return java.util.List<java.lang.String>
     */
    public static List<String> ftpUpload(FtpConfig config,String remotePath, List<File> files, boolean isDelete) {
        boolean uploadResult = false;
        List<String> fileNameList = new ArrayList<>();
        Ftp ftp = new Ftp(config, FtpMode.Active);

        for (File file : files) {
            uploadResult = ftp.upload(remotePath, file);
            if (uploadResult) {
                fileNameList.add(file.getName());
                log.info("监听上传FTP：{}-> {}", file.getAbsolutePath(), remotePath + "/" + file.getName());
                if (isDelete) {
                    FileUtil.del(file);
                }
            }
        }
        IoUtil.close(ftp);
        return fileNameList;
    }

}
